
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
			      console.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
						    Forrest Yu, 2005
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/

/*
	回车键: 把光标移到第一列
	换行键: 把光标前进到下一行
*/


#include "type.h"
#include "const.h"
#include "protect.h"
#include "string.h"
#include "proc.h"
#include "tty.h"
#include "console.h"
#include "global.h"
#include "keyboard.h"
#include "proto.h"

PRIVATE void set_cursor(unsigned int position);
PRIVATE void set_video_start_addr(u32 addr);
PRIVATE void flush(CONSOLE* p_con);

PRIVATE int Search_pos;
/*======================================================================*
			   init_screen
 *======================================================================*/
PUBLIC void init_screen(TTY* p_tty)
{
	int nr_tty = p_tty - tty_table;
	p_tty->p_console = console_table + nr_tty;

	int v_mem_size = V_MEM_SIZE >> 1;	/* 显存总大小 (in WORD) */

	int con_v_mem_size                   = v_mem_size / NR_CONSOLES;
	p_tty->p_console->original_addr      = nr_tty * con_v_mem_size;
	p_tty->p_console->v_mem_limit        = con_v_mem_size;
	p_tty->p_console->current_start_addr = p_tty->p_console->original_addr;

	/* 默认光标位置在最开始处 */
	p_tty->p_console->cursor = p_tty->p_console->original_addr;

	if (nr_tty == 0) {
		/* 第一个控制台沿用原来的光标位置 */
		//p_tty->p_console->cursor = disp_pos / 2;
		//disp_pos = 0;
		clear_screen(p_tty);
	}
	else {
		out_char(p_tty->p_console, nr_tty + '0');
		out_char(p_tty->p_console, '#');
	}

	set_cursor(p_tty->p_console->cursor);
}


/*======================================================================*
			   is_current_console
*======================================================================*/
PUBLIC int is_current_console(CONSOLE* p_con)
{
	return (p_con == &console_table[nr_current_console]);
}


/*======================================================================*
			   out_char
 *======================================================================*/
PUBLIC void out_char(CONSOLE* p_con, char ch)
{
	u8* p_vmem = (u8*)(V_MEM_BASE + p_con->cursor * 2);
	u8 COLOR = DEFAULT_CHAR_COLOR;
	if (mode == 1)
	{
		COLOR = SEARCH_MODE_COLOR;
	}
	
	switch(ch) {
	case '\n':
		if (mode==0)
		{
			if (p_con->cursor < p_con->original_addr +
		    p_con->v_mem_limit - SCREEN_WIDTH) {
				*p_vmem++ = '\n';
				*p_vmem++ = TAB_COLOR;
				p_con->cursor = p_con->original_addr + SCREEN_WIDTH * 
					((p_con->cursor - p_con->original_addr) /
				 	SCREEN_WIDTH + 1);
			}
		}
		else
		{
			mode = 2;
			search(p_con);
		}
		break;
	case '\b':
		if (mode!=2)
		{	
			if (p_con->cursor > p_con->original_addr) {
				if ((p_con->cursor>=p_con->original_addr+80) && ((p_con->cursor-p_con->original_addr)%80==0))
				{
					while (*(p_vmem-2)!='\n')
					{
						*(p_vmem-2) = 0;
						*(p_vmem-1) = COLOR;
						p_vmem--;
						p_vmem--;
						p_con->cursor--;
					}
					*(p_vmem-2) = 0;
						*(p_vmem-1) = COLOR;
						p_vmem--;
						p_vmem--;
						p_con->cursor--;
				}else
				{
					if (*(p_vmem-2)=='\t')
					{
						for (int i = 1; i <= 4; i++)
						{
							*(p_vmem-2*i) = 0;
							*(p_vmem-(2*i-1)) = COLOR;
							p_con->cursor--;
						}
					}else
					{
						p_con->cursor--;
						*(p_vmem-2) = 0;
						*(p_vmem-1) = COLOR;
					}
				}
			}
		}
		break;
	case '\t':
		if (p_con->cursor + 4 < p_con->original_addr + 
		p_con->v_mem_limit - SCREEN_WIDTH){
			if (mode==0)
			{
				for (int i = 0; i < 4; i++)
				{
					*p_vmem++ = '\t';
					*p_vmem++ = TAB_COLOR;
					p_con->cursor++;
				}
			}else{
				if (mode!=2)
				{
					for (int i = 0; i < 4; i++)
					{
						*p_vmem++ = '\t';
						*p_vmem++ = SEARCH_COLOR_FOR_TAB_AND_SPACR;
						p_con->cursor++;
					}
				}
			}
		}
		break;
	case ' ':
		if (mode!=2)
		{
			if (p_con->cursor <
		    p_con->original_addr + p_con->v_mem_limit - 1) {
				if (mode == 0)
				{
					*p_vmem++ = ' ';
					*p_vmem++ = COLOR;
					p_con->cursor++;
				}
				else
				{
					*p_vmem++ = ' ';
					*p_vmem++ = SEARCH_COLOR_FOR_TAB_AND_SPACR;
					p_con->cursor++;
				}
			}
		}
		break;
	case '\1':
		if (mode==0)
		{
			mode = 1; //enter search mode
			Search_pos = p_con->cursor;
		}else
		{
			mode = 0; //exit search mode
			int len  = p_con->cursor-Search_pos;
			*p_vmem--;
			for (int i = 0; i < len; i++)
			{
				*p_vmem-- = DEFAULT_CHAR_COLOR;
				*p_vmem-- = 0;
				p_con->cursor--;
			}
			recover(p_con);
		}
		break;
	default:
		if (mode!=2)
		{
			if (p_con->cursor <
		    p_con->original_addr + p_con->v_mem_limit - 1) {
				*p_vmem++ = ch;
				*p_vmem++ = COLOR;
				p_con->cursor++;
			}
		}
		break;
	}

	while (p_con->cursor >= p_con->current_start_addr + SCREEN_SIZE) {
		scroll_screen(p_con, SCR_DN);
	}

	flush(p_con);
}

/*======================================================================*
			   cus_out_char
 *======================================================================*/
PUBLIC void cus_out_char(CONSOLE* p_con, char ch, int color)
{
	u8* p_vmem = (u8*)(V_MEM_BASE + p_con->cursor * 2);
	u8 COLOR = DEFAULT_CHAR_COLOR;
	switch (ch)
	{
	case '\n':
		if (p_con->cursor < p_con->original_addr +
			p_con->v_mem_limit - SCREEN_WIDTH) {
			*p_vmem++ = '\n';
			*p_vmem++ = TAB_COLOR;
			p_con->cursor = p_con->original_addr + SCREEN_WIDTH * 
				((p_con->cursor - p_con->original_addr) /
				SCREEN_WIDTH + 1);
		}
		break;
	case '\b':
		if (p_con->cursor > p_con->original_addr) {
			if ((p_con->cursor>=p_con->original_addr+80) && ((p_con->cursor-p_con->original_addr)%80==0))
			{
				while (*(p_vmem-2)!='\n')
				{
					*(p_vmem-2) = 0;
					*(p_vmem-1) = COLOR;
					p_vmem--;
					p_vmem--;
					p_con->cursor--;
				}
				*(p_vmem-2) = 0;
					*(p_vmem-1) = COLOR;
					p_vmem--;
					p_vmem--;
					p_con->cursor--;
			}else
			{
				if (*(p_vmem-2)=='\t')
				{
					for (int i = 1; i <= 4; i++)
					{
						*(p_vmem-2*i) = 0;
						*(p_vmem-(2*i-1)) = COLOR;
						p_con->cursor--;
					}
				}else
				{
					p_con->cursor--;
					*(p_vmem-2) = 0;
					*(p_vmem-1) = COLOR;
				}
			}
		}
		break;
	default:
		if (p_con->cursor <
			p_con->original_addr + p_con->v_mem_limit - 1) {
			*p_vmem++ = ch;
			// #define COLOR_BLUE 0x01		/* 0000 0001 黑底蓝字 */
			// #define COLOR_GREEN 0x02		/* 0000 0010 黑底绿字 */
			// #define COLOR_LIGHTBLUE 0x03		/* 0000 0011 黑底淡蓝字 */
			// #define COLOR_RED 0x04		/* 0000 0100 黑底红字 */
			// #define COLOR_PINK 0x05		/* 0000 0101 黑底紫字 */
			// #define COLOR_YELLOW 0x06		/* 0000 0110 黑底黄字 */
			// #define COLOR_WHITE 0x07		/* 0000 0111 黑底白字 */
			if (color==1)
			{
				*p_vmem++ = COLOR_BLUE;
			}
			else if (color==2)
			{
				*p_vmem++ = COLOR_GREEN;
			}
			else if (color==3)
			{
				*p_vmem++ = COLOR_LIGHTBLUE;
			}
			else if (color==4)
			{
				*p_vmem++ = COLOR_RED;
			}
			else if (color==5)
			{
				*p_vmem++ = COLOR_PINK;
			}
			else if (color==6)
			{
				*p_vmem++ = COLOR_YELLOW;
			}
			else if (color==7)
			{
				*p_vmem++ = COLOR_WHITE;
			}
			p_con->cursor++;
		}
		break;
	}

	while (p_con->cursor >= p_con->current_start_addr + SCREEN_SIZE) {
		scroll_screen(p_con, SCR_DN);
	}

	flush(p_con);

}

/*======================================================================*
                           flush
*======================================================================*/
PRIVATE void flush(CONSOLE* p_con)
{
        set_cursor(p_con->cursor);
        set_video_start_addr(p_con->current_start_addr);
}

/*======================================================================*
			    set_cursor
 *======================================================================*/
PRIVATE void set_cursor(unsigned int position)
{
	disable_int();
	out_byte(CRTC_ADDR_REG, CURSOR_H);
	out_byte(CRTC_DATA_REG, (position >> 8) & 0xFF);
	out_byte(CRTC_ADDR_REG, CURSOR_L);
	out_byte(CRTC_DATA_REG, position & 0xFF);
	enable_int();
}

/*======================================================================*
			  set_video_start_addr
 *======================================================================*/
PRIVATE void set_video_start_addr(u32 addr)
{
	disable_int();
	out_byte(CRTC_ADDR_REG, START_ADDR_H);
	out_byte(CRTC_DATA_REG, (addr >> 8) & 0xFF);
	out_byte(CRTC_ADDR_REG, START_ADDR_L);
	out_byte(CRTC_DATA_REG, addr & 0xFF);
	enable_int();
}



/*======================================================================*
			   select_console
 *======================================================================*/
PUBLIC void select_console(int nr_console)	/* 0 ~ (NR_CONSOLES - 1) */
{
	if ((nr_console < 0) || (nr_console >= NR_CONSOLES)) {
		return;
	}

	nr_current_console = nr_console;

	set_cursor(console_table[nr_console].cursor);
	set_video_start_addr(console_table[nr_console].current_start_addr);
}

/*======================================================================*
			   scroll_screen
 *----------------------------------------------------------------------*
 滚屏.
 *----------------------------------------------------------------------*
 direction:
	SCR_UP	: 向上滚屏
	SCR_DN	: 向下滚屏
	其它	: 不做处理
 *======================================================================*/
PUBLIC void scroll_screen(CONSOLE* p_con, int direction)
{
	if (direction == SCR_UP) {
		if (p_con->current_start_addr > p_con->original_addr) {
			p_con->current_start_addr -= SCREEN_WIDTH;
		}
	}
	else if (direction == SCR_DN) {
		if (p_con->current_start_addr + SCREEN_SIZE <
		    p_con->original_addr + p_con->v_mem_limit) {
			p_con->current_start_addr += SCREEN_WIDTH;
		}
	}
	else{
	}

	set_video_start_addr(p_con->current_start_addr);
	set_cursor(p_con->cursor);
}

/*======================================================================*
			      	清除控制台内容
 *======================================================================*/
PUBLIC void clear_screen(TTY* p_tty){
	if (mode!=1 && mode!=2)
	{
		p_tty->p_console->cursor = p_tty->p_console->original_addr;
		p_tty->p_console->current_start_addr = p_tty->p_console->original_addr;
		for (unsigned int i = p_tty->p_console->original_addr; i < p_tty->p_console->v_mem_limit; i++)
		{
			out_char(p_tty->p_console, 0);
		}
		p_tty->p_console->cursor = p_tty->p_console->original_addr;
		p_tty->p_console->current_start_addr = p_tty->p_console->original_addr;
		set_video_start_addr(p_tty->p_console->current_start_addr);
		set_cursor(p_tty->p_console->cursor);
		disp_pos = 0;
	}
}

/*======================================================================*
			      	查找功能
 *======================================================================*/
PUBLIC void search(CONSOLE* p_con){
	u8 COLOR = SEARCH_MODE_COLOR;
	u8 COLOR_FOR_TAB = SEARCH_COLOR_FOR_TAB_AND_SPACR;
	u8 COLOR_FOR_SPACE = SEARCH_COLOR_FOR_TAB_AND_SPACR;
	int key_len = p_con->cursor-Search_pos;
	u8* p_vmem = (u8*)(V_MEM_BASE);
	int count = 0;
	for (int i = 0; i < Search_pos-key_len+1; i++)
	{
		for (int j = 0; j < key_len; j++)
		{
			if (*(p_vmem+(2*i+2*j))!=*(p_vmem+2*Search_pos+(2*j)))
			{
				break;
			}
			count++;
		}
		if (count==key_len)
		{
			for (int j = 0; j < key_len; j++)
			{
				if (*(p_vmem+(2*i+2*j))!='\t' && *(p_vmem+(2*i+2*j))!=' ')
				{
					*(p_vmem+(2*i+2*j+1)) = COLOR;
				}else if (*(p_vmem+(2*i+2*j))=='\t')
				{
					*(p_vmem+(2*i+2*j+1)) = COLOR_FOR_TAB;
				}else if (*(p_vmem+(2*i+2*j))==' ')
				{
					*(p_vmem+(2*i+2*j+1)) = COLOR_FOR_SPACE;
				}
			}
		}
		count = 0;
	}
}

/*
COLOR = DEFAULT_CHAR_COLOR;
		COLOR_FOR_TAB = TAB_COLOR;
		COLOR_FOR_SPACE = DEFAULT_CHAR_COLOR;
		*/

/*======================================================================*
			      	恢复字符颜色
 *======================================================================*/
PUBLIC void recover(CONSOLE* p_con){
	u8* COLOR = DEFAULT_CHAR_COLOR;
	u8* COLOR_FOR_TAB = TAB_COLOR;
	u8* COLOR_FOR_SPACE = DEFAULT_CHAR_COLOR;
	u8* p_vmem = (u8*)(V_MEM_BASE);
	for (int i = 0; i < Search_pos; i++)
	{
		if (*(p_vmem+2*i)!='\t' && *(p_vmem+2*i)!='\n')
		{
			*(p_vmem+2*i+1) = DEFAULT_CHAR_COLOR;
		}else
		{
			*(p_vmem+2*i+1) = TAB_COLOR;
		}
	}
}
